﻿using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using NLog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;
using WebMvc.Models;

// 引用业务层，复杂的逻辑由业务逻辑层实现后直接引用 

namespace WebMvc.Controllers
{
    [Produces("application/json")]    
    public abstract class BaseController<TEntity> : Controller where TEntity : BaseT, new()
    {
        protected readonly EFDbContext _context ;
        public BaseController(EFDbContext context)
        {
            _context = context;
            SetWhereLambda();
            SetPageSize();
            SetOrderLambda();
            SetIsASC();
        }
        //定义一记录日志的容器
        private static Logger Logger = LogManager.GetCurrentClassLogger();

        //基类的构造函数
        //public BaseController()
        //{            
        //    SetWhereLambda();
        //    SetPageSize();
        //    SetOrderLambda();
        //    SetIsASC();
        //}

        #region option        
        /// <summary>
        /// 定义用于查询多个结果的lambda表达式:o => o.Id>30
        /// </summary>
        protected Expression<Func<TEntity, bool>> WhereLambda { get; set; }
        public virtual void SetWhereLambda()  //子类可以实现，默认查出所有数据，特别对于大数据更应该进行过滤
        {
            WhereLambda = o => o.Id > 0;
        }

        //定义用于排序的字段
        protected Expression<Func<TEntity, object>> OrderLambda { get; set; }
        public virtual void SetOrderLambda()    //可以在子类中覆盖此方法，必须是实体中存在的(参考实体定义)，否则出错,例如Id，区分大小写
        {
            OrderLambda = o => o.SortIndex;
        }

        //定义是否按升序排列
        protected bool IsASC;
        public virtual void SetIsASC()  //可以在子类中覆盖此方法，设置isASC为false则按降序排
        {
            IsASC = true;
        }

        //定义每页显示条数
        protected int PageSize { get; set; }
        public virtual void SetPageSize()   //可以在子类中覆盖此方法，设置每页显示的条数
        {
            PageSize = 10;
        }
        #endregion

        #region 运用基本操作，实现页面，对应列表、增加、修改、删除（伪删除）
        // GET: Base
        public virtual async Task<IActionResult> Index()
        {
            return View(await _context.Set<TEntity>().ToListAsync());
        }

        // GET: Base/Details/5
        public async Task<IActionResult> Details(int id)
        {
            if (id.ToString() == null)
            {
                return NotFound();
            }

            var t = await GetById(id);
            if (t == null)
            {
                return NotFound();
            }

            return View(t);
        }

        // GET: KeywordType1/Create
        public IActionResult Create()
        {
            return View();
        }

        // POST: Base/Create
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create(TEntity t)
        {
            if (ModelState.IsValid)
            {
                await AddEntity(t);
                return RedirectToAction(nameof(Index));
            }
            return View(t);
        }

        // GET: Base/Edit/5
        public async Task<IActionResult> Edit(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var t = await GetById((int)id);
            if (t == null)
            {
                return NotFound();
            }
            return View(t);
        }

        // POST: Base/Edit/5
        // To protect from overposting attacks, please enable the specific properties you want to bind to     
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Edit(int id, TEntity t)
        {
            if (id != t.Id)
            {
                return NotFound();
            }

            if (ModelState.IsValid)
            {
                try
                {
                    await ModifyEntity(t);
                }
                catch (DbUpdateConcurrencyException)
                {
                    if (!TEntityExists(t.Id))
                    {
                        return NotFound();
                    }
                    else
                    {
                        throw;
                    }
                }
                return RedirectToAction(nameof(Index));
            }
            return View(t);
        }

        // GET: Base/Delete/5
        public async Task<IActionResult> Delete(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var t = await _context.Set<TEntity>().SingleOrDefaultAsync(m => m.Id == id);
            if (t == null)
            {
                return NotFound();
            }

            return View(t);
        }

        // POST: Base/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> DeleteConfirmed(int id)
        {
            var t = await GetById(id);
            await DeleteEntity(t); 
            return RedirectToAction(nameof(Index));
        }

        #endregion

        #region 运用基本操作，实现restful(api)，为了兼容以前的程序，此处命名不太规范
        //获取所有数据，对于大数据表，慎用，IEnumerable比List加载快，边显示边查，list要全部查完才显示
        public virtual IEnumerable<TEntity> GetAllListToJson()
        {
            return GetEntitysByLambdaAndSort(WhereLambda, OrderLambda, IsASC);
        }

        //分页获取数据
        public virtual IActionResult GetPagedListToJson(int? pageindex = 1)
        {
            int pageIndex = pageindex.Value;
            int rowCount = 0, pageCount = 0;
            var data = GetPagedEntitysByLambdaAndSort(pageIndex, PageSize, out rowCount, out pageCount, WhereLambda, OrderLambda, IsASC);

            //Json格式的要求{rowCount:22,pageCount:2,rows:{}},构造成Json的格式传递
            return Json(new
            {
                rowCount = rowCount,
                pageCount = pageCount,
                rows = data
            });
        }

        //根据Id获取数据
        public async Task<IActionResult> GetByIdToJson(int id)
        {
            TEntity t = await GetById(id);
            if (t == null)
            {
                return NotFound();
            }
            return Ok(t);
        }
        
        //对于有关联的信息，子类中注意要用override重写
        public virtual async Task<IActionResult> DeleteById(int id)
        {
            var userId = User.Identity.Name;  //获取登录时存储的用户名称(此处是user.id）
            var t = this.GetType();  //获取实例化的子类的类型
            if (!TEntityExists(id))
            {
                return NotFound();
            }
            try
            {
                var item = await GetById(id);
                await DeleteEntity(item);
                Logger.Info("-------------------------------删除-------------------------------------");
                Logger.Info("用户：" + userId + "，删除一条数据：" + t.Name + "-" + id);
                return Ok(new { Result = "Success" });
            }
            catch (Exception ex)
            {
                return Json(new { Result = "Faild", Message = ex.Message });
            }            
        }

        /// <summary>
        /// 批量删除，不提倡使用
        /// </summary>
        /// <param name="ids">包括id的字符串，结构为id,id,id,id</param>
        /// <returns></returns>
        public async Task<IActionResult> DeleteMutiByIds(string ids)
        {
            try
            {
                string[] idArray = ids.Split(',');
                List<int> delIds = new List<int>();
                foreach (string id in idArray)
                {
                    await DeleteById(int.Parse(id));
                }
                return Json(new { Result = "Success" });
            }
            catch (Exception ex)
            {
                return Json(new
                {
                    Result = "Faild",
                    Message = ex.Message
                });
            }
        }
                
        public async Task<IActionResult> AddOrModify(TEntity item)
        {
            var userId = User.Identity.Name;  //获取登录时存储的用户名称(此处是user.id）            
            var type = item.GetType();  //获取实例化的子类的类型
            //if (!ModelState.IsValid)
            //{
            //    return Json(new
            //    {
            //        Result = "Faild",
            //        Message = GetModelStateError()
            //    });
            //}
            try
            {
                if (item.Id == 0)
                {
                    item.CreatedBy = int.Parse(userId);
                    item.CreatedOn = DateTime.Now;
                    item.UpdatedBy = int.Parse(userId);
                    item.UpdatedOn = DateTime.Now;

                    await AddEntity(item);                   
                    
                    //CurrentEntityService.AddEntity(item);
                    Logger.Info("-------------------------------增加----------------------------------");
                    Logger.Info("用户：" + userId + "，增加一条数据:" + type.Name + "-" + item.Id + "。具体数据：");
                    //反射，属性名必须有get,set方法
                    //用Type.GetProperties获得PropertyInfo[],然后就可以用foreach 遍历了
                    foreach (PropertyInfo pi in type.GetProperties())
                    {
                        string tempName = pi.Name;  //获得属性的名字   
                        var tempVal = "";
                        try
                        {
                            tempVal = pi.GetValue(item).ToString();//用pi.GetValue获得值，若值不存在，则要引发异常 
                        }
                        catch
                        {
                            tempVal = "未赋值";
                        }
                        Logger.Info(tempName + ":" + tempVal);
                    }
                    return CreatedAtAction("Get", new { id = item.Id }, item);
                }
                else
                {
                    item.UpdatedBy = int.Parse(userId);
                    item.UpdatedOn = DateTime.Now;
                                        
                    await ModifyEntity(item);

                    Logger.Info("-------------------------------修改-------------------------------------");
                    Logger.Info("用户：" + userId + "，修改一条数据:" + type.Name + "-" + item.Id + "。具体数据：");
                    //用Type.GetProperties获得PropertyInfo[],然后就可以用foreach 遍历了
                    foreach (PropertyInfo pi in type.GetProperties())
                    {
                        string tempName = pi.Name;//获得属性的名字
                        var tempVal = "";
                        try
                        {
                            tempVal = pi.GetValue(item).ToString();//用pi.GetValue获得值，若值不存在，则要引发异常 
                        }
                        catch
                        {
                            tempVal = "未赋值";
                        }
                        Logger.Info(tempName + ":" + tempVal);
                    }
                }
                return Json(new { Result = "Success" });
            }
            catch (Exception ex)
            {
                return Json(new { Result = "Faild", Message = ex.Message });
            }
        }
        
        private bool TEntityExists(int id)
        {
            return _context.Set<TEntity>().Any(e => e.Id == id);
        }
        #endregion

        #region 基本的操作（增删查改），异步操作

        #region 增加实体
        /// <summary>
        /// 实现对数据的添加功能
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public virtual async Task AddEntity(TEntity entity)
        {
            _context.Set<TEntity>().Add(entity);
            await _context.SaveChangesAsync();
        }
        #endregion

        #region 修改实体
        /// <summary>
        /// 实现对数据的修改功能
        /// </summary>
        /// <param name="entity"></param>
        /// <returns>实体</returns>
        public virtual async Task ModifyEntity(TEntity entity)
        {
            _context.Set<TEntity>().Attach(entity);   //将对象附加到EF 容器 
            _context.Entry(entity).State = EntityState.Modified;
            await _context.SaveChangesAsync();
        }
        #endregion

        #region 删除实体（伪删除）
        /// <summary>
        /// 删除一个实体对象，用修改的方式实现伪删除
        /// </summary>
        /// <param name="entity">必须包含要删除id的对象</param>
        /// <returns></returns>
        public virtual async Task DeleteEntity(TEntity entity)
        {
            entity.IsDeleted = true;
            await ModifyEntity(entity);

            //_context.Set<TEntity>().Attach(entity);
            //_context.Entry(entity).State = EntityState.Deleted;
            //await _context.SaveChangesAsync();
        }

        /// <summary>
        /// 根据条件对数据批量删除,用修改的方法实现伪删除
        /// </summary>
        /// <param name="whereLambda">条件表达式</param>
        public virtual async Task DeleteEntitysByLambda(Expression<Func<TEntity, bool>> whereLambda)
        {
            var entitys = _context.Set<TEntity>().Where(whereLambda).ToList();
            foreach (TEntity entity in entitys)
            {
                entity.IsDeleted = true;
                _context.Entry(entity).State = EntityState.Modified;
                await _context.SaveChangesAsync();
            }
        }
        #endregion

        #region 查找实体 IQueryable只存贮数据库操作语句，不立即运行，从而可以实现后继修改数据库操作语句
        /// <summary>
        /// 根据Id查找实体
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public virtual async Task<TEntity> GetById(int id)
        {
            return await _context.Set<TEntity>().SingleOrDefaultAsync(m => m.Id == id);
        }

        /// <summary>
        /// 实现条件查询多个实体并排序
        /// </summary>
        /// <typeparam name="S">按照某个类进行排序</typeparam>
        /// <param name="whereLambda">取得查询的条件</param>
        /// <param name="isAsc">取得排序的条件，true升序</param>
        /// <param name="orderByLambd">根据那个字段进行排序</param>
        /// <returns></returns>
        public IQueryable<TEntity> GetEntitysByLambdaAndSort(Expression<Func<TEntity, bool>> whereLambda, Expression<Func<TEntity, object>> orderLambda, bool isAsc)
        {            
            var query = _context.Set<TEntity>().Where(whereLambda).Where(o => o.IsDeleted == false);
            if (isAsc)
                query = query.OrderBy(orderLambda);
            else
                query = query.OrderByDescending(orderLambda);
            return query;           
        }

        /// <summary>
        /// 实现对数据的分页查询，输出总页数及总条数
        /// </summary>
        /// <param name="pageIndex">当前第几页</param> 
        /// <param name="pageSize">一页显示多少条数据</param> 
        /// <param name="rowCount">总条数</param> 
        /// /// <param name="pageCount">总页数</param> 
        /// <param name="whereLambda">获取数据的lambda条件</param> 
        /// <param name="isAsc">是否按升序排</param> 
        /// <param name="OrderByProperty">根据那个字段进行排序</param> 
        /// <returns></returns>  
        public IQueryable<TEntity> GetPagedEntitysByLambdaAndSort(int pageIndex, int pageSize, out int rowCount, out int pageCount, Expression<Func<TEntity, bool>> whereLambda, Expression<Func<TEntity, object>> orderLambda, bool isAsc)
        {
            var query = _context.Set<TEntity>().Where(whereLambda).Where(o => o.IsDeleted == false);
            rowCount = query.Count();   //得到总的条数
            if (rowCount % pageSize == 0)
            {
                pageCount = rowCount / pageSize;
            }
            else
            {
                pageCount = rowCount / pageSize + 1;
            }

            if (isAsc)
                query = query.OrderBy(orderLambda);
            else
                query = query.OrderByDescending(orderLambda);

            return query.Skip((pageIndex - 1) * pageSize).Take(pageSize);

        }
        #endregion

        #endregion

    }
}
